#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#include <sys/ktrace.h>

#include "kdump.h"

extern int optind;
extern char *optarg;

void		init(t_args* args)
{
  args->file = strdup(DEFAULT_FILENAME);
  args->d = 0;
  args->h = 0;
  args->pid = 0;
  args->s = 0;
  args->E = 0;
  args->l = 0;
  args->R = 0;
  args->r = 0;
  args->T = 0;
  args->n = 0;
  args->m = -1;
}

t_args*		init_args(int ac, char **av)
{
  char		ch;
  t_args	*args;

  args = malloc(sizeof(*args));
  init(args);
  while ((ch = getopt(ac, av, OPT_STRING)) != -1)
    {
      switch (ch)
	{
	case 'f':
	  if (optind > ac)
	    {
	      fprintf(stderr, "RTFM");
	      exit(0);
	    }
	  args->file = av[optind];
	  break;
	case 'd':
	  args->d = 1;
	  break;
	case 'H':
	  args->h = 1;
	  break;
	case 'p':
	  if (optind > ac)
	    {
	      fprintf(stderr, "RTFM");
	      exit(0);
	    }
	  args->pid = atoi(av[optind]);
	  break;
	case 's':
	  args->s = 1;
	  break;
	case 'l':
	  args->l = 1;
	  break;
	case 'E':
	  args->E = 1;
	  break;
	case 'T':
	  args->T = 1;
	  break;
	case 'R':
	  args->R = 1;
	  break;
	case 'r':
	  args->r = 1;
	  break;
	case 'n':
	  args->n = 1;
	  break;
	case 'm':
	  if (optind >= ac)
	    {
	      fprintf(stderr, "RTFM");
	      exit(0);
	    }
	  args->m = atoi(av[optind]);
	  break;
	default:
	  break;
	}
    }
  return (args);
}

void	  print_space(int size)
{
  int i;

  i = 0;
  while (i < size)
    {
      printf(" ");
      i++;
    }
}


int			main(int ac, char **av)
{
  struct ktr_header	kd;
  struct timeval	t;
  static struct timeval	prev;
  static struct timeval	first;
  int			i;
  int			flag;
  int			fd;
  char			buff[BUFF_SIZE];
  t_args		*args;
  t_pt			fct[] =
    {
      {KTR_SYSRET, kd_sysret},
      {KTR_SYSCALL, kd_syscall},
      {KTR_NAMEI, kd_namei},
      {KTR_GENIO, kd_genio},
      {KTR_PSIG, kd_psig},
      {KTR_CSW, kd_csw},
      {KTR_USER, kd_user},
      {KTR_SYSCTL, kd_sysctl},
      {KTR_STRUCT, kd_struct},
      {0, 0}
    };

  args = init_args(ac, av);
  if ((fd = open(args->file, O_RDONLY)) == -1)
    {
      fprintf(stderr, "kdump: %s: %s\n", args->file, strerror(errno));
      exit(EXIT_FAILURE);
    }
  flag = 0;
  while (read(fd, &kd, sizeof(kd)) > 0)
    {
      if (!args->pid || args->pid == kd.ktr_pid)
	{
	  if (!args->h)
	    printf("  %d %s", kd.ktr_pid, kd.ktr_comm);
	  else
	    printf("%d %d\t%s", kd.ktr_pid, kd.ktr_tid, kd.ktr_comm);
	  if (args->T)
	    printf("%d.%06ld", kd.ktr_time.tv_sec, kd.ktr_time.tv_usec);
	  print_space(9- strlen(kd.ktr_comm));
	  if (args->E)
	    {
	      if (flag == 0)
		first = kd.ktr_time;
	      timersub(&kd.ktr_time, &first, &t);
	      first = kd.ktr_time;
	      if (flag == 0)
		{
		  printf("%d.%06ld\t", t.tv_sec, t.tv_usec);
		  flag = 1;
		}
	      else
		printf("%d.%06ld\t", prev.tv_sec + t.tv_sec, prev.tv_usec + t.tv_usec);
	      prev.tv_sec += t.tv_sec;
	      prev.tv_usec += t.tv_usec;
	    }
	  if (args->R)
	    {
	      timersub(&kd.ktr_time, &first, &t);
	      first = kd.ktr_time;
	      printf("%d.%06ld\t", t.tv_sec, t.tv_usec);
	    }
	  i = 0;
	  while (fct[i].a != 0)
	    {
	      if (fct[i].a == kd.ktr_type)
		{
		  fct[i].func(kd, fd, args);
		  break;
		}
	      i++;
	    }
	}
    }
  if (args->l)
    while (read(0, buff, BUFF_SIZE) > 0);
  return (0);
}
